package com.siyu.model.dispatch.utils;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author xinchan
 * @version 1.0.1 2022-04-24
 */
public class CalculateUtils {
    /**
     * 计算超标准或者超汛限水位的时段
     *
     * @param input    水库 28 个时段的水位
     * @param standard 标准
     * @return
     */
    public static String overWaterLevel(List<Double> input, double standard, String startTime) throws ParseException {
        StringBuilder builder = new StringBuilder();
        int size = input.size();
        for (int i = 0; i < size; i++) {
            if (input.get(i) > standard) {
                builder.append(i).append(",");
            }
        }
        return CalculateUtils.printNums(builder.toString(), startTime);
    }

    /**
     * 时段数字简化
     *
     * @param s
     * @return
     */
    public static String printNums(String s, String startTime) throws ParseException {
        if (s.equals("")) {
            return "--";
        }
        int[] nums = Arrays.stream(s.split(",")).mapToInt(Integer::parseInt).toArray();
        StringBuilder builder = new StringBuilder();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date;
        Calendar instance;
        int pre = nums[0];
        int n = nums.length - 1;
        for (int i = 0; i < n; ++i) {
            if (nums[i + 1] != nums[i] + 1) {
                date = simpleDateFormat.parse(startTime);
                instance = Calendar.getInstance();
                instance.setTime(date);
                instance.add(Calendar.HOUR, pre);
                Date date1 = instance.getTime();
                instance.add(Calendar.HOUR, nums[i] - pre);
                Date date2 = instance.getTime();
                builder.append(simpleDateFormat.format(date1)).append("-").append(simpleDateFormat.format(date2)).append(",");
                pre = nums[i + 1];
            }
        }
        date = simpleDateFormat.parse(startTime);
        instance = Calendar.getInstance();
        instance.setTime(date);
        instance.add(Calendar.HOUR, pre);
        Date date1 = instance.getTime();
        if (pre == nums[n]) {
            return builder + String.format("%s", simpleDateFormat.format(date1));
        }
        instance.add(Calendar.HOUR, nums[n] - pre);
        Date date2 = instance.getTime();
        return builder + String.format("%s-%s", simpleDateFormat.format(date1), simpleDateFormat.format(date2));
    }

    /**
     * 插值函数 由流量计算水位
     *
     * @param Q
     * @param curveLQ
     * @return
     */
    public static double getDownLevelFromLQ(double Q, double[][] curveLQ) {
        int CurveLQLength = curveLQ.length;
        double Level = 0;
        int w = 0;
        if (Q <= curveLQ[0][1]) Level = curveLQ[0][0];
        else if (Q >= curveLQ[CurveLQLength - 1][1])
            Level = curveLQ[CurveLQLength - 1][0] - (curveLQ[CurveLQLength - 1][1] - Q) * (curveLQ[CurveLQLength - 2][0] - curveLQ[CurveLQLength - 1][0]) / (curveLQ[CurveLQLength - 2][1] - curveLQ[CurveLQLength - 1][1]);
        else {
            for (w = 1; w < CurveLQLength; w++) {
                if (curveLQ[w][1] >= Q) break;
            }
            Level = curveLQ[w - 1][0] - (curveLQ[w - 1][0] - curveLQ[w][0]) * (curveLQ[w - 1][1] - Q) / (curveLQ[w - 1][1] - curveLQ[w][1]);
        }
        return Level;
    }

    /**
     * 插值函数：根据流域出口断面流量计算水位
     *
     * @param Q
     * @param name
     * @return
     */
    public static double getDownLevelFromLQInChukou(double Q, String name) {
        double[][] curveLQ;
        if (name.equals("yagang-liuxihe")) {  // 流溪河
            curveLQ = new double[][]{{-5.1, 5.4}, {-4.1, 105.9}, {-3.1, 286.5}, {-2.1, 527.1}, {-1.1, 817.5},
                    {-0.1, 1150.8}, {0.90, 1521.7}, {1.9, 1926.3}, {2.9, 2361.2}, {3.2, 2500.0}, {3.9, 2823.8},
                    {4.9, 3311.7}, {5.9, 3822.9}};
        } else if (name.equals("yagang-bainihe")) {  // 白泥河
            curveLQ = new double[][]{{-11.8, 4.2}, {-10.3, 140.4}, {-8.8, 381.5}, {-7.3, 689.7}, {-5.8, 1047.8},
                    {-4.3, 1445.7}, {-2.8, 1877.1}, {-1.3, 2337.4}, {0.2, 2823.6}, {1.7, 3333.4}, {3.2, 3865.1},
                    {4.7, 4417.5}};
        } else if (name.equals("dasheng-zengjiang")) {  // 增江
            curveLQ = new double[][]{{-3.6, 14.9}, {-2.8, 216.2}, {-2.6, 292.2}, {-1.8, 677.7}, {-1.6, 792.3},
                    {-0.8, 1315.3}, {-0.6, 1461.0}, {0.2, 2098.3}, {0.4, 2270.4}, {1.2, 3006.7}, {1.4, 3202.0},
                    {2.2, 4025.8}};
        } else if (name.equals("dasheng-xifuhe")) { // 西福河
            curveLQ = new double[][]{{-3.6, 14.9}, {-2.8, 216.2}, {-2.6, 292.2}, {-1.8, 677.7}, {-1.6, 792.3},
                    {-0.8, 1315.3}, {-0.6, 1461.0}, {0.2, 2098.3}, {0.4, 2270.4}, {1.2, 3006.7}, {1.4, 3202.0},
                    {2.2, 4025.8}};
        } else {
            throw new RuntimeException("name 匹配不到！！");
        }
        return getDownLevelFromLQ(Q, curveLQ);
    }

    /**
     * 插值函数：根据闸坝流量计算水位
     *
     * @param Q
     * @param name
     * @return
     */
    public static double getDownLevelFromLQInSluiceInput(double Q, String name) {
        double[][] curveLQ;

        if (Objects.equals(name, "良口")) {
            curveLQ = new double[][]{{63.3, 585}, {63.43, 657}, {66, 1541}, {66.69, 1821}, {67.04, 2004}};
        } else if (Objects.equals(name, "塘料")) {
            curveLQ = new double[][]{{54.68, 863}, {55.24, 1025}, {56.66, 1557}, {57.21, 1818}, {57.72, 2077}};
        } else if (Objects.equals(name, "卫东")) {
            curveLQ = new double[][]{{49.01, 875}, {49.57, 1120}, {50.56, 1608}, {51.04, 1870}, {51.53, 2129}};
        } else if (Objects.equals(name, "大坳")) {
            curveLQ = new double[][]{{25.4, 1400}, {25.8, 1700}, {26.2, 1980}};
        } else if (Objects.equals(name, "牛心岭")) {
            curveLQ = new double[][]{{15.9, 849}, {16, 994}, {16.74, 1220}, {18.57, 1730}, {19.53, 1921}, {20.41, 2080}, {21.04, 2350}};
        } else if (Objects.equals(name, "李溪")) {
            curveLQ = new double[][]{{9.7, 600}, {10.2, 1000}, {10.5, 1400}, {11.05, 2000}, {11.4, 2400}};
        } else if (Objects.equals(name, "人和")) {
            curveLQ = new double[][]{{4.25, 1200}, {4.9, 1500}, {6, 2100}, {6.2, 2250}, {6.7, 2550}};
        } else if (Objects.equals(name, "初溪")) {
            curveLQ = new double[][]{{7.11, 2266}, {7.9, 2841.1}, {8.56, 3416.3}, {9.22, 4165.9}, {10.16, 4723.6}};
        } else if (Objects.equals(name, "正果")) {
            // TODO: 暂时没有数据，不处理
            return 0;
        } else {
            throw new RuntimeException("name 匹配不到！！");
        }
        return CalculateUtils.getDownLevelFromLQ(Q, curveLQ);
    }

    /**
     * 插值函数 由水头计算耗水率
     *
     * @param H
     * @param CurveHR
     * @param CurveHRLength
     * @return
     */
    public static double getRateFromHR(double H, double[][] CurveHR, int CurveHRLength) {
        double Rate = 0;// 耗水率
        int w = 0;
        if (H <= CurveHR[0][0]) Rate = CurveHR[0][1];
        else if (H >= CurveHR[CurveHRLength - 1][0])
            Rate = CurveHR[CurveHRLength - 1][1] - (CurveHR[CurveHRLength - 1][0] - H) * (CurveHR[CurveHRLength - 2][1] - CurveHR[CurveHRLength - 1][1]) / (CurveHR[CurveHRLength - 2][0] - CurveHR[CurveHRLength - 1][0]);
        else {
            for (w = 1; w < CurveHRLength; w++) {
                if (CurveHR[w][0] >= H) break;
                else continue;
            }
            Rate = CurveHR[w - 1][1] - (CurveHR[w - 1][1] - CurveHR[w][1]) * (CurveHR[w - 1][0] - H) / (CurveHR[w - 1][0] - CurveHR[w][0]);
        }

        return Rate;
    }

    /**
     * 插值函数 由水位计算库容
     *
     * @param Level
     * @param CurveLV
     * @param CurveLVLength
     * @return
     */
    public static double getVolumeFromLV(double Level, double[][] CurveLV, int CurveLVLength) {
        double Volume = 0;
        int w = 0;
        if (Level <= CurveLV[0][0]) Volume = CurveLV[0][1];
        else if (Level >= CurveLV[CurveLVLength - 1][0])
            Volume = CurveLV[CurveLVLength - 1][1] - (CurveLV[CurveLVLength - 1][0] - Level) * (CurveLV[CurveLVLength - 2][1] - CurveLV[CurveLVLength - 1][1]) / (CurveLV[CurveLVLength - 2][0] - CurveLV[CurveLVLength - 1][0]);
        else {
            for (w = 1; w < CurveLVLength; w++) {
                if (CurveLV[w][0] >= Level) break;
                else continue;
            }
            Volume = CurveLV[w - 1][1] - (CurveLV[w - 1][1] - CurveLV[w][1]) * (CurveLV[w - 1][0] - Level) / (CurveLV[w - 1][0] - CurveLV[w][0]);
        }
        return Volume;
    }

    /**
     * 插值函数 由库容计算水位
     *
     * @param Volume
     * @param CurveLV
     * @param CurveLVLength
     * @return
     */
    public static double getLevelFromLV(double Volume, double[][] CurveLV, int CurveLVLength) {
        double Level = 0;
        int w = 0;
        if (Volume <= CurveLV[0][1]) Level = CurveLV[0][0];
        else if (Volume >= CurveLV[CurveLVLength - 1][1])
            Level = CurveLV[CurveLVLength - 1][0] - (CurveLV[CurveLVLength - 1][1] - Volume) * (CurveLV[CurveLVLength - 2][0] - CurveLV[CurveLVLength - 1][0]) / (CurveLV[CurveLVLength - 2][1] - CurveLV[CurveLVLength - 1][1]);
        else {
            for (w = 1; w < CurveLVLength; w++) {
                if (CurveLV[w][1] >= Volume) break;
                else continue;
            }
            Level = CurveLV[w - 1][0] - (CurveLV[w - 1][0] - CurveLV[w][0]) * (CurveLV[w - 1][1] - Volume) / (CurveLV[w - 1][1] - CurveLV[w][1]);
        }
        return Level;
    }

    /**
     * 插值函数 由水位计算最大下泄能力
     *
     * @param Level
     * @param CurveLQ
     * @param CurveLQLength
     * @return
     */
    public static double getMaxQreleaseFromLQ(double Level, double[][] CurveLQ, int CurveLQLength) {
        double MaxQrelease = 0;
        int w = 0;
        if (Level <= CurveLQ[0][0]) MaxQrelease = CurveLQ[0][1];
        else if (Level >= CurveLQ[CurveLQLength - 1][0])
            MaxQrelease = CurveLQ[CurveLQLength - 1][1] - (CurveLQ[CurveLQLength - 1][0] - Level) * (CurveLQ[CurveLQLength - 2][1] - CurveLQ[CurveLQLength - 1][1]) / (CurveLQ[CurveLQLength - 2][0] - CurveLQ[CurveLQLength - 1][0]);
        else {
            for (w = 1; w < CurveLQLength; w++) {
                if (CurveLQ[w][0] >= Level) break;
                else continue;
            }
            MaxQrelease = CurveLQ[w - 1][1] - (CurveLQ[w - 1][1] - CurveLQ[w][1]) * (CurveLQ[w - 1][0] - Level) / (CurveLQ[w - 1][0] - CurveLQ[w][0]);
        }

        return MaxQrelease;
    }

    /**
     * 插值函数 由下泄流量计算下游水位
     *
     * @param Q
     * @param CurveDLQ
     * @param CurveDLQLength
     * @return
     */
    public static double getDownLevelFromLQ(double Q, double[][] CurveDLQ, int CurveDLQLength) {
        double DownLevel = 0;
        int w = 0;
        if (Q <= CurveDLQ[0][1]) DownLevel = CurveDLQ[0][0];
        else if (Q >= CurveDLQ[CurveDLQLength - 1][1])
            DownLevel = CurveDLQ[CurveDLQLength - 1][0] - (CurveDLQ[CurveDLQLength - 1][1] - Q) * (CurveDLQ[CurveDLQLength - 2][0] - CurveDLQ[CurveDLQLength - 1][0]) / (CurveDLQ[CurveDLQLength - 2][1] - CurveDLQ[CurveDLQLength - 1][1]);
        else {
            for (w = 1; w < CurveDLQLength; w++) {
                if (CurveDLQ[w][1] >= Q) break;
                else continue;
            }
            DownLevel = CurveDLQ[w - 1][0] - (CurveDLQ[w - 1][0] - CurveDLQ[w][0]) * (CurveDLQ[w - 1][1] - Q) / (CurveDLQ[w - 1][1] - CurveDLQ[w][1]);
        }
        return DownLevel;
    }

    /**
     * 获取数组最小值
     *
     * @param TempOptimalRemainBenefit
     * @param DiscretePointNum
     * @return
     */
    public static double getMinValue(double[] TempOptimalRemainBenefit, int DiscretePointNum) {
        double MinValue = 0;

        MinValue = TempOptimalRemainBenefit[0];

        for (int m = 1; m <= DiscretePointNum; m++) {
            if (MinValue > TempOptimalRemainBenefit[m]) {
                MinValue = TempOptimalRemainBenefit[m];
            }
        }
        return MinValue;
    }

    /**
     * 获取数组最小值下标
     *
     * @param TempOptimalRemainBenefit
     * @param DiscretePointNum
     * @return
     */
    public static int getMinValueIndex(double[] TempOptimalRemainBenefit, int DiscretePointNum) {
        int MinValueIndex = 0;

        double MinValue = 0;
        MinValue = TempOptimalRemainBenefit[0];

        for (int m = 1; m <= DiscretePointNum; m++) {
            if (MinValue > TempOptimalRemainBenefit[m]) {
                MinValue = TempOptimalRemainBenefit[m];
                MinValueIndex = m;
            }
        }
        return MinValueIndex;

    }

    /**
     * 获取数组最大值
     *
     * @param Value
     * @return
     */
    public static double getMaxValue(double[] Value) {
        return Arrays.stream(Value).max().getAsDouble();
    }

    /**
     * 获取数组最大值下标
     *
     * @param Value
     * @return
     */
    public static int getMaxValueIndex(double[] Value) {
        double MaxValue = Value[0];
        int MaxValueIndex = 0;
        for (int i = 1; i < Value.length; i++) {
            if (MaxValue < Value[i]) {
                MaxValue = Value[i];
                MaxValueIndex = i;
            }
        }
        return MaxValueIndex;
    }

    /**
     * 获取二维数组的最大值
     *
     * @param matrix 二维数组
     * @return
     */
    public static double getMaxValueOfMatrix(double[][] matrix) {
        double maxValue = Double.NEGATIVE_INFINITY;
        for (double[] row : matrix) {
            for (double data : row) {
                if (maxValue < data) {
                    maxValue = data;
                }
            }
        }
        return maxValue;
    }

    /**
     * 获取二维数组的最大值下标
     *
     * @param matrix 二维数组
     * @return
     */
    public static int getMaxValueOfMatrixIndex(double[][] matrix) {
        double maxValue = Double.NEGATIVE_INFINITY;
        int maxIndex = 0;
        for (double[] doubles : matrix) {
            for (int j = 0; j < matrix[0].length; j++) {
                if (maxValue < doubles[j]) {
                    maxValue = doubles[j];
                    maxIndex = j;
                }
            }
        }
        return maxIndex;
    }


    public static List<Double> setIntervalRunoffData(List<Double> downstreamSectionInflow, List<Double> intervalRunoffData) {
        List<Double> Temp_IntervalRunoffData = new ArrayList<>();
        int size = downstreamSectionInflow.size();
        for (int i = 0; i < size; i++) {
            Temp_IntervalRunoffData.add(downstreamSectionInflow.get(i) + intervalRunoffData.get(i));
        }
        return Temp_IntervalRunoffData;  // 前面没有输入的
    }
}
